package org.jmc.refmanager.domain.service.impl;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;
import javax.persistence.PersistenceException;

import org.jmc.refmanager.domain.dao.ChangeDao;
import org.jmc.refmanager.domain.dao.OrgUnitDao;
import org.jmc.refmanager.domain.dao.QueryParam;
import org.jmc.refmanager.domain.dao.UserDao;
import org.jmc.refmanager.domain.model.Change;
import org.jmc.refmanager.domain.model.OrgUnit;
import org.jmc.refmanager.domain.model.Role;
import org.jmc.refmanager.domain.model.User;
import org.jmc.refmanager.domain.service.ChangeManager;
import org.jmc.refmanager.domain.service.UserManager;
import org.jmc.refmanager.domain.service.exception.BusinessException;
import org.jmc.refmanager.security.SecurityHelper;
import org.jmc.refmanager.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service("userManager")
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
public class UserManagerImpl implements UserManager {
	private static final Logger logger = LoggerFactory.getLogger(UserManager.class);
	
	@Resource(name="userDao")
	private UserDao userDao;

	@Resource(name="orgUnitDao")
	private OrgUnitDao orgUnitDao;

	@Resource(name="taskDao")
	private ChangeDao taskDao;

	@Resource(name="taskManager")
	private ChangeManager taskManager;

	@Override
	public List<User> list(QueryParam queryParam) {
		List<User> users = null;
		
		if (SecurityHelper.isApprover()) {
			// Limiter les utilisateurs appartenant e la personne concernee
			String approverName = SecurityHelper.getAuthentication().getName();
			users = userDao.findTeam(queryParam, approverName);
		} else if (SecurityHelper.isAdministrator()) {
			// Retourner tous les utilisateurs
			users = userDao.findAllAndPaging(queryParam);
		}
		
		return users;
	}

	@Override
	public Integer count() {
		if (SecurityHelper.isApprover()) {
			String approverName = SecurityHelper.getAuthentication().getName();
			return userDao.count(userDao.createNamedQuery(User.COUNT_BY_OWNER), approverName);
		} else if (SecurityHelper.isAdministrator()) {
			return userDao.countAll();
		}
		
		return 0;
	}

	@Override
	public List<User> listByMatricule(final QueryParam queryParam, final String matricule) {
		List<User> users = null;

		String matriculeLikeFilter = Util.isEmptyOrBlank(matricule)?"%":matricule.concat("%");
		if (SecurityHelper.isApprover()) {
			// Limiter les utilisateurs appartenant e la personne concernee
			String approverName = SecurityHelper.getAuthentication().getName();
			users = userDao.findAndPaging(queryParam, userDao.createNamedQuery(User.FIND_BY_OWNER_AND_MATRICULE), approverName, matriculeLikeFilter);
		} else if (SecurityHelper.isAdministrator()) {
			// Retourner tous les utilisateurs
			users = userDao.findAndPaging(queryParam, userDao.createNamedQuery(User.FIND_BY_MATRICULE), matriculeLikeFilter);
		}
		
		return users;
	}

	@Override
	public Integer countByMatricule(final String matricule) {
		String matriculeLikeFilter = Util.isEmptyOrBlank(matricule)?"%":matricule.concat("%");
		if (SecurityHelper.isApprover()) {
			String approverName = SecurityHelper.getAuthentication().getName();
			return userDao.count(userDao.createNamedQuery(User.COUNT_BY_OWNER_AND_MATRICULE), approverName, matriculeLikeFilter);
		} else if (SecurityHelper.isAdministrator()) {
			return userDao.count(userDao.createNamedQuery(User.COUNT_BY_MATRICULE), matriculeLikeFilter);
		}
		
		return 0;
	}

	@Override
	public Set<User> findApproversOfService(OrgUnit service) {
		// Recherche de la direction de validation
		OrgUnit direction = orgUnitDao.getDirectionValidation(service);
		
		return findApproversOfDirection(direction);
	}
	
	@Override
	public Set<User> findApproversOfDirection(OrgUnit direction) {
		Set<User> approvers = new HashSet<User>();
		
		List<User> users = userDao.findInScope(direction);
		approvers.addAll(users);

		return approvers;
	}

	@Override
	@Transactional(readOnly = false)
	public void persist(User transientUser) {
		// TODO , propagation = Propagation.REQUIRES_NEW ?
		try {
			// Assigner les teches e l'utilisateur si c'est un referent
			if (SecurityHelper.ROLE_APPROVER.equals(transientUser.getRole().getName())) {
				// Rechercher toutes les teches non approuvees ou rejetees
				// dans sa direction de validation
				List<Change> tasks = taskDao.findGroupTasks(transientUser.getValidationScope().getId());
				for (Change change : tasks) {
					transientUser.addTask(change);
				}
			}

			userDao.persist(transientUser);
		} catch (PersistenceException e) {
			// TODO Traiter les exceptions jusqu'e trouver la root cause (sqle)
			throw new BusinessException(e.getMessage(), e);
		}
		
	}

	@Override
	@Transactional(readOnly = false)
	public void remove(User persistentUser) {
		Set<Change> myTasks = persistentUser.getMyTasks();
		if (myTasks != null) {
			for (Change change : myTasks) {
				// Releche les teches reservees par l'utilisateur
				if (persistentUser.equals(change.getLockedByUser())) {
					taskManager.revert(change, persistentUser);
				}
			}
			
			// Suppression des teches assignees
			persistentUser.setMyTasks(null);
			
		}
		userDao.remove(persistentUser);
	}

	@Override
	@Transactional(readOnly = false)
	public void remove(Integer userId) {
		User user = userDao.find(userId);
		this.remove(user);
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.MANDATORY)
	public User update(User detachedUser) {
		return userDao.merge(detachedUser);
	}

	@Override
	public User findById(Integer id) {
		return userDao.find(id);
	}

	@Override
	public List<User> findAll() {
		return userDao.findAll();
	}

	@Override
	@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
	public void changePassword(User user, String oldPassword, String newPassword) {
		user.setPassword(newPassword);
		userDao.persist(user);
	}

	@Override
	public void updateRole(User user, Role newRole, OrgUnit scope) {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException("Fonction non implémentée");
	}

	@Override
	public void updateScope(User approver, OrgUnit newScope) {
		// TODO Auto-generated method stub
		throw new UnsupportedOperationException("Fonction non implémentée");
	}

	@Override
	public User getByMatricule(String matricule) {
		return userDao.getByMatricule(matricule);
	}

}
